package com.pets.web.service.impl;

import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONWriter;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.toolkit.LambdaUtils;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.pets.common.utils.SecurityUtils;
import com.pets.common.utils.StringUtils;
import com.pets.web.config.WXPayConfig;
import com.pets.web.dto.AllSumDto;
import com.pets.web.dto.CreateOrderDto;
import com.pets.web.dto.SearchOrderDto;
import com.pets.web.entity.PetsGoods;
import com.pets.web.entity.PetsOrders;
import com.pets.web.entity.PetsShop;
import com.pets.web.entity.PetsUser;
import com.pets.web.enums.OrderStatusEnums;
import com.pets.web.enums.OrderTypeEnums;
import com.pets.web.mapper.PetOrdersMapper;
import com.pets.web.service.IPetOrdersService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pets.web.service.IPetsGoodsService;
import com.pets.web.service.IPetsUserService;
import com.pets.web.vo.ApiPetOrdersSearchRequest;
import com.pets.web.vo.CreateOrderRequest;
import com.pets.web.vo.PetOrdersSearchRequest;
import com.pets.web.vo.SearchOrderRequest;
import com.wechat.pay.java.core.RSAAutoCertificateConfig;
import com.wechat.pay.java.core.RSAPublicKeyConfig;
import com.wechat.pay.java.core.exception.ServiceException;
import com.wechat.pay.java.core.exception.ValidationException;
import com.wechat.pay.java.core.notification.NotificationParser;
import com.wechat.pay.java.core.notification.RequestParam;
import com.wechat.pay.java.service.payments.jsapi.JsapiServiceExtension;
import com.wechat.pay.java.service.payments.jsapi.model.Amount;
import com.wechat.pay.java.service.payments.jsapi.model.Payer;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayRequest;
import com.wechat.pay.java.service.payments.jsapi.model.PrepayWithRequestPaymentResponse;
import com.wechat.pay.java.service.payments.model.Transaction;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author zhurenwang
 * @since 2024-05-26
 */
@Slf4j
@Service
public class PetOrdersServiceImpl extends ServiceImpl<PetOrdersMapper, PetsOrders> implements IPetOrdersService {

    @Autowired
    private IPetsUserService petsUserService;
    @Autowired
    private IPetsGoodsService petsGoodsService;
    @Autowired
    private WXPayConfig wxPayConfig;
    @Value("${wx.appId}")
    private String appId;

    //只初始化一次 后台有线程会一直刷新证书
    private RSAPublicKeyConfig config;

    @PostConstruct
    private void initConfig() {
        if (Objects.isNull(config)) {
            config = new RSAPublicKeyConfig.Builder()
                    .merchantId(wxPayConfig.merchantId)
                    .privateKeyFromPath(wxPayConfig.privateKeyPath)
                    .merchantSerialNumber(wxPayConfig.merchantSerialNumber)
                    .apiV3Key(wxPayConfig.apiV3Key)
                    .publicKeyFromPath(wxPayConfig.publicKeyPath)
                    .publicKeyId(wxPayConfig.publicKeyId)
                    .build();
            log.info(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>微信支付证书初始化完成");
        }
    }

    @Override
    public List<PetsOrders> list(PetOrdersSearchRequest request) {
        return lambdaQuery()
                .eq(Objects.nonNull(request.getOpenId()), PetsOrders::getOpenId, request.getOpenId())
                .like(StringUtils.isNotEmpty(request.getNickname()), PetsOrders::getNickname, request.getNickname())
                .eq(StringUtils.isNotEmpty(request.getOrderNo()), PetsOrders::getOrderNo, request.getOrderNo())
                .eq(Objects.nonNull(request.getType()), PetsOrders::getType, request.getType())
                .eq(Objects.nonNull(request.getGoodsId()), PetsOrders::getGoodsId, request.getGoodsId())
                .like(StringUtils.isNotEmpty(request.getGoodsName()), PetsOrders::getGoodsName, request.getGoodsName())
                .eq(Objects.nonNull(request.getPayAmount()), PetsOrders::getPayAmount, request.getPayAmount())
                .eq(Objects.nonNull(request.getWeight()), PetsOrders::getWeight, request.getWeight())
                .in(Objects.nonNull(request.getStatus()), PetsOrders::getStatus, request.getStatus())
                .eq(Objects.nonNull(request.getSysUserId()), PetsOrders::getSysUserId, request.getSysUserId())
                .eq(StringUtils.isNotEmpty(request.getPfCode()), PetsOrders::getPfCode, request.getPfCode())
                .between((Objects.nonNull(request.getStartTime()) && Objects.nonNull(request.getEndTime())), PetsOrders::getCreateTime, request.getStartTime(), request.getEndTime())
                .orderByDesc(PetsOrders::getCreateTime)
                .list();
    }

    @Override
    public List<PetsOrders> list(ApiPetOrdersSearchRequest request) {
        MPJLambdaWrapper<PetsOrders> wrapper = JoinWrappers.lambda(PetsOrders.class)
                .selectAll(PetsOrders.class)
                .selectAs(PetsShop::getName, PetsOrders::getShopName)
                .selectAs(PetsShop::getAvatar, PetsOrders::getShopAvatar)
                .selectAs(PetsGoods::getMainImage, PetsOrders::getMainImage)
                .leftJoin(PetsGoods.class, PetsGoods::getId, PetsOrders::getGoodsId)
                .leftJoin(PetsShop.class, PetsShop::getId, PetsGoods::getShopId)
                .eq(PetsOrders::getOpenId, request.getOpenId())
                .eq(Objects.nonNull(request.getStatus()), PetsOrders::getStatus, request.getStatus())
                .orderByDesc(PetsOrders::getCreateTime);
        return baseMapper.selectJoinList(PetsOrders.class, wrapper);
    }

    @Override
    public PetsOrders selectByOrderNo(String orderNo) {
        return lambdaQuery().eq(PetsOrders::getOrderNo, orderNo).one();
    }

    @Override
    public PetsOrders selectByUserId(Integer userId, Integer goodsId) {
        return lambdaQuery()
                .eq(PetsOrders::getUserId, userId)
                .eq(PetsOrders::getGoodsId, goodsId)
                .notIn(PetsOrders::getStatus, OrderStatusEnums.NO_PAY).one();
    }

    @Override
    public AllSumDto sumAll(Integer goodsId) {
        Long sysUserId = null;
        if (!SecurityUtils.isAdmin(SecurityUtils.getUserId())) {
            sysUserId = SecurityUtils.getUserId();
        }
        MPJLambdaWrapper<PetsOrders> wrapper = JoinWrappers.lambda(PetsOrders.class)
                .selectSum(PetsOrders::getPayAmount, AllSumDto::getAllAmount)
                .selectSum(PetsOrders::getWeight, AllSumDto::getAllWeight)
                .selectCount(PetsOrders::getId, AllSumDto::getAllNum)
                .notIn(PetsOrders::getStatus, OrderStatusEnums.NO_PAY)
                .eq(Objects.nonNull(goodsId), PetsOrders::getGoodsId, goodsId)
                .eq(Objects.nonNull(sysUserId), PetsOrders::getSysUserId, sysUserId);
        return baseMapper.selectJoinOne(AllSumDto.class, wrapper);
    }

    @Override
    public Long sumWeightByGoodsId(Integer goodsId) {
        return baseMapper.sumWeightByGoodsId(goodsId);
    }

    @Override
    public List<PetsOrders> recordList(Integer goodsId) {
        return lambdaQuery()
                .eq(PetsOrders::getGoodsId, goodsId)
                .notIn(PetsOrders::getStatus, OrderStatusEnums.NO_PAY)
                .orderByDesc(PetsOrders::getCreateTime)
                .list();
    }

    @Override
    public int deleteById(Integer id) {
        return baseMapper.deleteById(id);
    }

    @Override
    public SearchOrderDto searchOrder(SearchOrderRequest request) {
        MPJLambdaWrapper<PetsOrders> wrapper = JoinWrappers.lambda(PetsOrders.class)
                .selectSum(PetsOrders::getPayAmount, SearchOrderDto::getAmount)
                .selectCount(PetsOrders::getId, SearchOrderDto::getOrderNum)
                .eq(PetsOrders::getStatus, OrderStatusEnums.PAYED)
                .eq(PetsOrders::getGoodsId, request.getGoodsId())
                .between(PetsOrders::getCreateTime, request.getStartDate(), request.getEndDate());
        SearchOrderDto dto = baseMapper.selectJoinOne(SearchOrderDto.class, wrapper);
        List<PetsOrders> list = lambdaQuery()
                .select(PetsOrders::getId)
                .between(PetsOrders::getCreateTime, request.getStartDate(), request.getEndDate())
                .eq(PetsOrders::getStatus, OrderStatusEnums.PAYED)
                .eq(PetsOrders::getGoodsId, request.getGoodsId())
                .list();
        List<String> ids = list.stream().map(PetsOrders::getId).map(String::valueOf).sorted().collect(Collectors.toList());
        dto.setOrderIds(String.join(",", ids));
        dto.setType(ids.size()>1?1:0);
        return dto;
    }

    @Override
    public void updateByIds(String orderIds) {
        String[] idsStr = orderIds.split(",");
        List<Integer> ids = Arrays.stream(idsStr).map(Integer::parseInt).collect(Collectors.toList());
        lambdaUpdate()
                .set(PetsOrders::getStatus, OrderStatusEnums.DELIVERY)
                .in(PetsOrders::getId, ids)
                .update();
    }

    @Override
    public Long countByGoodsId(Integer goodsId) {
        QueryWrapper<PetsOrders> wrapper = new QueryWrapper<>();
        wrapper.select("id");
        wrapper.eq("goods_id", goodsId);
        wrapper.notIn("status", OrderStatusEnums.NO_PAY);
        return baseMapper.selectCount(wrapper);
    }

    @Override
    public List<PetsOrders> getOrderList(Integer goodsId) {
        return lambdaQuery()
                .select(PetsOrders::getAvatar, PetsOrders::getCreateTime)
                .eq(PetsOrders::getGoodsId, goodsId)
                .notIn(PetsOrders::getStatus, OrderStatusEnums.NO_PAY)
                .orderByDesc(PetsOrders::getCreateTime)
                .last("limit 5").list();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateOrderDto create(CreateOrderRequest request) {
        PetsOrders orders = createOrder(request);
        // 构建service
        JsapiServiceExtension service = new JsapiServiceExtension.Builder().config(config).build();
        // request.setXxx(val)设置所需参数，具体参数可见Request定义
        PrepayRequest prepayRequest = new PrepayRequest();
        Amount amount = new Amount();
        amount.setTotal(orders.getPayAmount().multiply(new BigDecimal(100)).intValue());
        //amount.setTotal(1);
        prepayRequest.setAmount(amount);
        prepayRequest.setAppid(appId);
        prepayRequest.setMchid(wxPayConfig.merchantId);
        prepayRequest.setDescription("商品订单");
        prepayRequest.setNotifyUrl(wxPayConfig.notifyUrl);
        //request.setAttach(helperScrambleOrderIncome.getTitle());
        Payer payer = new Payer();
        payer.setOpenid(orders.getOpenId());
        prepayRequest.setPayer(payer);
        prepayRequest.setOutTradeNo(orders.getOrderNo());
        prepayRequest.setAttach("牛啊牛！");
        log.info("创建订单: {}", JSONObject.toJSONString(prepayRequest, JSONWriter.Feature.PrettyFormat));
        // 调用下单方法，得到应答
        PrepayWithRequestPaymentResponse response = service.prepayWithRequestPayment(prepayRequest);
        CreateOrderDto dto = new CreateOrderDto();
        dto.setOrderNo(orders.getOrderNo());
        dto.setResponse(response);
        return dto;
    }

    private PetsOrders createOrder(CreateOrderRequest request) {
        PetsUser user = petsUserService.getByOpenId(request.getOpenId());
        Objects.requireNonNull(user);
        PetsGoods goods = petsGoodsService.getById(request.getGoodsId());
        Objects.requireNonNull(goods);
        PetsOrders order = new PetsOrders();
        order.setOrderNo(System.currentTimeMillis() + RandomUtil.randomNumbers(3));
        order.setType(request.getType()==0? OrderTypeEnums.FOR_BES: OrderTypeEnums.FOR_SELF);
        order.setUserId(user.getId());
        order.setOpenId(user.getOpenId());
        order.setNickname(user.getNickname());
        order.setAvatar(user.getHeadImgUrl());
        order.setGoodsId(goods.getId());
        order.setGoodsName(goods.getName());
        order.setGoodsPrice(goods.getPrice());
        order.setNum(request.getWeight());
        BigDecimal payAmount = goods.getPrice().multiply(new BigDecimal(request.getWeight()));
        order.setPayAmount(payAmount);
        order.setWeight(request.getWeight());
        order.setStatus(OrderStatusEnums.NO_PAY);
        order.setName(request.getName());
        order.setPhone(request.getPhone());
        order.setAddress(request.getAddress());
        order.setPfCode(request.getPfCode());
        order.setSysUserId(goods.getSysUserId());
        baseMapper.insert(order);
        return order;
    }

    public ResponseEntity<Void> notify(HttpServletRequest request, String body) {
        //效验签名
        String wechatpaySignature = request.getHeader("Wechatpay-Signature");
        String wechatpaySerial = request.getHeader("Wechatpay-Serial");
        String wechatpayNonce = request.getHeader("Wechatpay-Nonce");
        String wechatpayTimestamp = request.getHeader("Wechatpay-Timestamp");
        String wechatpaySignatureType = request.getHeader("Wechatpay-Signature-Type");
        // 构造 RequestParam
        RequestParam requestParam = new RequestParam.Builder()
                .serialNumber(wechatpaySerial)
                .nonce(wechatpayNonce)
                .signature(wechatpaySignature)
                .timestamp(wechatpayTimestamp)
                .body(body)
                .build();
        // 初始化 NotificationParser
        NotificationParser parser = new NotificationParser(config);
        try {
            // 以支付通知回调为例，验签、解密并转换成 Transaction
            Transaction transaction = parser.parse(requestParam, Transaction.class);
            //业务处理
            businessProcess(transaction);
        } catch (ValidationException e) {
            // 签名验证失败，返回 401 UNAUTHORIZED 状态码
            log.error("sign verification failed", e);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build();
        }catch (Exception e) {
            log.info("接收回调业务处理异常", e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
        }
        return ResponseEntity.status(HttpStatus.OK).build();
    }

    private void businessProcess(Transaction transaction) {
        //log.info("开始支付回调业务处理 transaction: {}", JSONObject.toJSONString(transaction, JSONWriter.Feature.PrettyFormat));
        //订单表维护支付状态
        String orderNo = transaction.getOutTradeNo();
        lambdaUpdate()
                .set(PetsOrders::getStatus, OrderStatusEnums.PAYED)
                .set(PetsOrders::getUpdateTime, LocalDateTime.now())
                .eq(PetsOrders::getOrderNo, orderNo)
                .eq(PetsOrders::getStatus, OrderStatusEnums.NO_PAY)
                .update();
        //日志
        log.info("接收订单支付回调: {}", JSONObject.toJSONString(transaction, JSONWriter.Feature.PrettyFormat));
    }
}
